#include <stdio.h>    // 包含标准输入输出库，用于基本的输入输出操作
#include <stdlib.h>   // 包含标准库，用于内存分配等操作
#include <string.h>   // 包含字符串操作库，用于字符串处理
#include <unistd.h>   // 包含unistd库，用于系统调用
#include <wiringPi.h> // 包含wiringPi库，用于GPIO控制
#include <pthread.h>  // 包含pthread库，用于线程控制

#include "garbage.h"    // 包含自定义的垃圾回收库，用于内存管理
#include "serialTest.h" // 包含自定义的串口工具库，用于串口通信
#include "pwm.h"        // 包含自定义的PWM库，用于控制舵机

int serial_fd;         // 串口文件描述符
pthread_mutex_t mutex; // 互斥锁，用于线程之间的互斥访问
pthread_cond_t cond;   // 条件变量，用于线程之间的条件同步

/*判断进程是否在运行*/
static int detect_process(const char *process_name)
{
    int n = -1;
    FILE *strm;
    char buf[128] = {0};

    // 构造命令字符串，通过ps命令查找进程
    sprintf(buf, "ps -ax | grep %s|grep -v grep", process_name);
    // 使用popen执行命令并读取输出
    if ((strm = popen(buf, "r")) != NULL)
    {
        if (fgets(buf, sizeof(buf), strm) != NULL)
        {
            n = atoi(buf); // 将进程ID字符串转换为整数
        }
    }
    else
    {
        return -1; // popen失败
    }
    pclose(strm); // 关闭popen打开的文件流

    return n;
}

/*语音识别线程*/
void *SU03T_func(void *arg)
{
    unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0x55, 0xAA}; // 发送数据
    int len = 0;                                                    // 串口数据长度

    if (serial_fd == -1)
    {
        printf("%s|%s|%d：串口未打开\n", __FILE__, __func__, __LINE__);
        pthread_exit(NULL); // 线程退出
    }
    while (1)
    {
        len = mySerialReadString(serial_fd, buffer);            // 读取串口数据
        printf("len = %d, buffer[2] = 0x%x\n", len, buffer[2]); // 打印串口数据
        // 检测到垃圾分类数据，唤醒阿里云交互线程
        if (len > 0 && buffer[2] == 0x46)
        {
            pthread_mutex_lock(&mutex);   // 加互斥锁
            buffer[2] = 0x00;             // 置0x46为0x00
            pthread_cond_signal(&cond);   // 唤醒阿里云交互线程
            pthread_mutex_unlock(&mutex); // 解互斥锁
        }
    }

    pthread_exit(0); // 线程退出
}

/* 语音播报线程 */
void *yuyin_func(void *arg)
{
    pthread_detach(pthread_self());               // 使线程成为守护线程
    unsigned char *buffer = (unsigned char *)arg; // 接收到的数据

    if (serial_fd == -1)
    {
        printf("%s|%s|%d：串口未打开\n", __FILE__, __func__, __LINE__);
        pthread_exit(NULL); // 线程退出
    }

    if (buffer != NULL)
    { // 判断是否接收到数据
        printf("开始播报语音...\n");
        mySerialSendString(serial_fd, buffer, 6); // 发送数据
        printf("播报语音结束\n");
    }

    pthread_exit(0); // 线程退出
}

/* SG90舵机线程 */
void *SG90_func(void *arg)
{
    pthread_detach(pthread_self());               // 分离线程，使其在退出时能够自动释放资源
    unsigned char *buffer = (unsigned char *)arg; // 接收到的数据

    if (buffer[2] == 0x41)
    { // 识别为干垃圾
        pwm_stop(GanGarbage_SG90_PIN);
        delay(2000); // 延时2秒
        pwm_write(GanGarbage_SG90_PIN);
    }
    else if (buffer[2] == 0x42)
    { // 识别为湿垃圾
        pwm_stop(ShiGarbage_SG90_PIN);
        delay(2000); // 延时2秒
        pwm_write(ShiGarbage_SG90_PIN);
    }
    else if (buffer[2] == 0x43)
    { // 识别为可回收垃圾
        pwm_stop(KeGarbage_SG90_PIN);
        delay(2000); // 延时2秒
        pwm_write(KeGarbage_SG90_PIN);
    }
    else if (buffer[2] == 0x44)
    { // 识别为有害垃圾
        pwm_stop(YouGarbage_SG90_PIN);
        delay(2000); // 延时2秒
        pwm_write(YouGarbage_SG90_PIN);
    }

    pthread_exit(0); // 线程退出
}

/*阿里云交互线程*/
void *aliyun_func(void *arg)
{
    unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0x55, 0xAA}; // 发送数据
    char *category = NULL;                                          // 垃圾分类结果
    pthread_t SG90_thread_id;                                       // 定义SG90舵机线程ID
    pthread_t yuyin_thread_id;                                      // 定义播报线程ID

    while (1)
    {
        pthread_mutex_lock(&mutex);       // 加互斥锁
        pthread_cond_wait(&cond, &mutex); // 等待SU03T线程发送垃圾分类数据
        pthread_mutex_unlock(&mutex);     // 解互斥锁

        buffer[2] = 0x00; // 置0x00为0x46
        printf("使用wget拍照中...\n");
        system(WGET_CMD); // 执行wget命令拍照
        printf("拍照完成\n");

        if (access(GARBAGE_FILE, F_OK) == 0)
        {                                          // 判断垃圾分类文件是否存在
            category = garbage_category(category); // 分类垃圾
            if (strstr(category, "干垃圾"))
            {
                buffer[2] = 0x41; // 识别为干垃圾
            }
            else if (strstr(category, "湿垃圾"))
            {
                buffer[2] = 0x42; // 识别为湿垃圾
            }
            else if (strstr(category, "可回收垃圾"))
            {
                buffer[2] = 0x43; // 识别为可回收垃圾
            }
            else if (strstr(category, "有害垃圾"))
            {
                buffer[2] = 0x44; // 识别为有害垃圾
            }
            else
            {
                buffer[2] = 0x45; // 未识别到垃圾类型
            }
        }
        else
        {
            buffer[2] = 0x45; // 识别失败
        }
        // 创建开垃圾桶线程
        pthread_create(&SG90_thread_id, NULL, SG90_func, (void *)buffer); // 创建SG90舵机线程
        // 创建语音播报线程
        pthread_create(&yuyin_thread_id, NULL, yuyin_func, (void *)buffer); // 创建播报线程

        remove(GARBAGE_FILE); // 删除垃圾分类文件
    }

    pthread_exit(NULL); // 线程退出
}

int main(int argc, char **argv)
{
    int ret;

    pthread_t SU03T_thread_id;  // 定义SU03T线程ID
    pthread_t aliyun_thread_id; // 定义阿里云交互线程ID

    wiringPiSetup(); // 初始化wiringPi库
    garbage_init();  // 初始化垃圾分类器

    ret = detect_process("mjpg_streamer"); // 检测mjpg_streamer是否在运行
    if (ret == -1)
    {
        printf("USB摄像头未连接或mjpg_streamer未启动\n");
        goto END; // 未启动则退出
    }

    serial_fd = mySerialOpen(SERIAL_DEV, SERIAL_BAUD); // 打开串口
    if (serial_fd == -1)
    {
        printf("打开串口失败\n");
        goto END; // 打开串口失败则退出
    }

    // 创建语音线程
    pthread_create(&SU03T_thread_id, NULL, SU03T_func, NULL); // 创建SU03T线程
    // 创建阿里云交互线程
    pthread_create(&aliyun_thread_id, NULL, aliyun_func, NULL); // 创建阿里云交互线程

    pthread_join(SU03T_thread_id, NULL);  // 等待SU03T线程结束
    pthread_join(aliyun_thread_id, NULL); // 等待阿里云交互线程结束

    pthread_mutex_destroy(&mutex); // 销毁互斥锁
    pthread_cond_destroy(&cond);   // 销毁条件变量

    close(serial_fd); // 关闭串口

END:
    garbage_destroy(); // 销毁垃圾分类器

    return 0;
}
